package model;

import lock.ConcreteLock;
import lock.Lock;

public class Philosopher implements Runnable{

	private PhilosopherState state;
	private final int number;
	private final PhilosopherProtocol protocol;
	private boolean stopped;
	private boolean stopInitiated;
	private final Lock stateLock = new ConcreteLock(false);

	public Lock getStateLock() {
		return stateLock;
	}

	/**
	 * Changes the state of this Philosopher.
	 * Notifies the {@link PTOMonitor} when the new state is either {@link Thinking} or {@link Eating}.
	 * @param state is the new state.
	 */
	protected void setState(final PhilosopherState state) {
		this.state = state;
		state.accept(new PhilosopherStateVisitor() {
			
			@Override
			public void visit(final Thinking thinking) {
				PTOMonitor.getInstance().notifyStateChange(thinking, Philosopher.this);
			}
			
			@Override
			public void visit(final LeftTokenObtained leftTokenObtained) {}
			
			@Override
			public void visit(final Eating eating) {
				PTOMonitor.getInstance().notifyStateChange(eating, Philosopher.this);
			}

			@Override
			public void visit(final StopEating stopEating) {}
		});
	}
	
	protected Philosopher(final int number,final PhilosopherProtocol protocol) {
		this.state = Thinking.getInstance();
		this.number = number;
		this.protocol = protocol;
		this.protocol.setPhilosopher(this);
	}
	
	@Override
	public void run() {
		while(!stopped){
			try{
				this.protocol.waitAndChangeState();
			}
			catch (final InterruptedException e) {
				stopped = true;
			}
		}
	}

	public int getNumber() {
		return number;
	}

	public PhilosopherState getState() {
		return this.state;
	}

	public void stop() {
		this.stopInitiated = true;
	}

	public boolean isEating() {
		return this.state instanceof Eating;
	}
	
	@Override
	public String toString(){
		return "(" + this.number + " : " + this.state + ")";
	}

	public boolean isStopInitiated() {
		return stopInitiated;
	}

	public void setStopped(final boolean stopped) {
		this.stopped = stopped;
	}

}
